/// <license>
/// Copyright (c) 2002-2008, DotNetNuke Corporation
/// All rights reserved
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
/// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
/// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
/// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
/// of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
/// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
/// DEALINGS IN THE SOFTWARE.
/// </license>

#region Using Statements

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;

using DotNetNuke.Modules.Articles.Common;
using Constants = DotNetNuke.Modules.Articles.Common.Constants;
using DotNetNuke.Modules.Articles.Framework;

#endregion

namespace DotNetNuke.Modules.Articles.UI.Controls
{
    /// -----------------------------------------------------------------------------
    /// <summary>
    /// The ArticlesTreeView skin object displays the contents of the module, in a treeview
    /// </summary>
    /// <history>
    /// 	[cnurse]	03/21/2006	created
    /// </history>
    /// -----------------------------------------------------------------------------
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
    ToolboxData("<{0}:ArticlesTreeView runat=\"server\"> </{0}:ArticlesTreeView>"),
    ParseChildren(true)]
    public class ArticlesTreeView : CollectionSkinObjectBase, INamingContainer
    {
        #region Controls

        private TreeView _TreeView;

        #endregion

        #region Private Members

        private int _TruncateNodeLength;

        #endregion

        #region Public Properties

        [Category("TreeView"),
        DefaultValue(0),
        Description("The maximum Node Length.")]
        public int TruncateNodeLength
        {
            get { return _TruncateNodeLength; }
            set { _TruncateNodeLength = value; }
        }

        #region Style Properties

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to the Node when the mouse hovers over it.
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to the Node when the mouse hovers over it.")]
        public Style HoverNodeStyle
        {
            get
            {
                EnsureChildControls();
                return _TreeView.HoverNodeStyle;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to a Leaf Node
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to a LeafNode.")]
        public TreeNodeStyle LeafNodeStyle
        {
            get
            {
                EnsureChildControls();
                return _TreeView.LeafNodeStyle;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The collection of Level Styles
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The collection of Level Styles.")]
        public TreeNodeStyleCollection LevelStyles
        {
            get
            {
                EnsureChildControls();
                return _TreeView.LevelStyles;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to a TreeView Node
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to a Node.")]
        public TreeNodeStyle NodeStyle
        {
            get 
            {
                EnsureChildControls();
                return _TreeView.NodeStyle;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to the parent node
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to the parent Node.")]
        public TreeNodeStyle ParentNodeStyle
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ParentNodeStyle;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to the root Node
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to the root Node.")]
        public TreeNodeStyle RootNodeStyle
        {
            get
            {
                EnsureChildControls();
                return _TreeView.RootNodeStyle;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// The Style to apply to the selected Node
        /// </summary>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        [Category("Style"),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        PersistenceMode(PersistenceMode.InnerProperty),
        NotifyParentProperty(true),
        TypeConverter(typeof(ExpandableObjectConverter)),
        Description("The Style to apply to the selected Node.")]
        public TreeNodeStyle SelectedNodeStyle
        {
            get
            {
                EnsureChildControls();
                return _TreeView.SelectedNodeStyle;
            }
        }

        #endregion

        #region TreeView Properties

        [Category("TreeView"),
        DefaultValue(""),
        Description("The url of the image to use for collapsable nodes")]
        public string CollapseImageUrl
        {
            get
            {
                EnsureChildControls();
                return _TreeView.CollapseImageUrl;
            }
            set
            {
                EnsureChildControls();
                _TreeView.CollapseImageUrl = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(0),
        Description("The initial Expansion Depth of the tree.")]
        public int ExpandDepth
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ExpandDepth;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ExpandDepth = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(""),
        Description("The url of the image to use for expandable nodes")]
        public string ExpandImageUrl
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ExpandImageUrl;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ExpandImageUrl = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(TreeViewImageSet.Simple),
        Description("The image set to use for the TreeView Images")]
        public TreeViewImageSet ImageSet
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ImageSet;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ImageSet = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(""),
        Description("The url of the folder where the line images are stored.")]
        public string LineImagesFolder
        {
            get
            {
                EnsureChildControls();
                return _TreeView.LineImagesFolder;
            }
            set
            {
                EnsureChildControls();
                _TreeView.LineImagesFolder = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(20),
        Description("The amount to indent a Node")]
        public int NodeIndent
        {
            get
            {
                EnsureChildControls();
                return _TreeView.NodeIndent;
            }
            set
            {
                EnsureChildControls();
                _TreeView.NodeIndent = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(false),
        Description("True to wrap text, otherwise false.")]
        public bool NodeWrap
        {
            get
            {
                EnsureChildControls();
                return _TreeView.NodeWrap;
            }
            set
            {
                EnsureChildControls();
                _TreeView.NodeWrap = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(""),
        Description("The url of the image to use for non-expandable nodes")]
        public string NoExpandImageUrl
        {
            get
            {
                EnsureChildControls();
                return _TreeView.NoExpandImageUrl;
            }
            set
            {
                EnsureChildControls();
                _TreeView.NoExpandImageUrl = value;
            }
        }

        [Category("TreeView"),
        DefaultValue("/"),
        Description("The string to use as a path separator for the nodes.")]
        public char PathSeparator
        {
            get
            {
                EnsureChildControls();
                return _TreeView.PathSeparator;
            }
            set
            {
                EnsureChildControls();
                _TreeView.PathSeparator = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(false),
        Description("A flag that determines whether to load the nodes from the client.")]
        public bool PopulateNodesFromClient
        {
            get
            {
                EnsureChildControls();
                return _TreeView.PopulateNodesFromClient;
            }
            set
            {
                EnsureChildControls();
                _TreeView.PopulateNodesFromClient = value;
            }
        }

        [Browsable(false)]
        public TreeNode SelectedNode
        {
            get
            {
                EnsureChildControls();
                return _TreeView.SelectedNode;
            }
        }

        [Category("TreeView"),
        DefaultValue(false),
        Description("A setting that determines whether to Show Check boxes.")]
        public TreeNodeTypes ShowCheckBoxes
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ShowCheckBoxes;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ShowCheckBoxes = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(false),
        Description("A flag that determines whether to Show Tree Lines.")]
        public bool ShowLines
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ShowLines;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ShowLines = value;
            }
        }

        [Category("TreeView"),
        DefaultValue(true),
        Description("A flag that determines whether expansion indicators are shown.")]
        public bool ShowExpandCollapse
        {
            get
            {
                EnsureChildControls();
                return _TreeView.ShowExpandCollapse;
            }
            set
            {
                EnsureChildControls();
                _TreeView.ShowExpandCollapse = value;
            }
        }

        #endregion

        #endregion

        #region Private Methods

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// AddArticleNode adds an article to the TreeView nodes collection
        /// </summary>
        /// <param name="nodes">The nodes collection</param>
        /// <param name="article">The article to add</param>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void AddArticleNode(TreeNodeCollection nodes, ArticleInfo article)
        {
            TreeNode newNode = new TreeNode();
            string articleTitle = article.Title;
            if (TruncateNodeLength > 0 && articleTitle.Length > TruncateNodeLength)
                articleTitle = articleTitle.Substring(0, TruncateNodeLength);
            newNode.Text = articleTitle;
            newNode.Value = "article" + article.ArticleId.ToString();
            newNode.ToolTip = article.Title;
            newNode.NavigateUrl = ParentArticlesModule.ArticleURL(TabId, article.ArticleId, Null.NullInteger, Constants.KEY_ViewArticle, true);

            // Set additional properties for the node.
            newNode.SelectAction = TreeNodeSelectAction.Select;

            // Add the new node to the nodes collection.
            nodes.Add(newNode);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// AddSectionNode adds a section to the TreeView nodes collection
        /// </summary>
        /// <param name="nodes">The nodes collection</param>
        /// <param name="section">The section to add</param>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void AddSectionNode(TreeNodeCollection nodes, SectionInfo section)
        {
            TreeNode newNode = new TreeNode();
            newNode.Text = section.Name;
            newNode.Value = "section" + section.SectionId.ToString();
            newNode.ToolTip = section.Name;
            newNode.NavigateUrl = ParentArticlesModule.ArticleURL(TabId, Null.NullInteger, section.SectionId, Constants.KEY_ViewSection, true);

            PopulateChildNodes(newNode);

            // Set additional properties for the node.
            newNode.SelectAction = TreeNodeSelectAction.SelectExpand;

            // Add the new node to the nodes collection.
            nodes.Add(newNode);
        }
      
        /// -----------------------------------------------------------------------------
        /// <summary>
        /// CreateDesignTimeNodes creates the nodes to display in Deisgn Time
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void CreateDesignTimeNodes()
        {
            TreeNode rootNode = new TreeNode();
            rootNode.Text = "Root Section";

            TreeNode sectionNode = new TreeNode();
            sectionNode.Text = "Section 1";
            rootNode.ChildNodes.Add(sectionNode);

            TreeNode articleNode = new TreeNode();
            articleNode.Text = "Article 1";
            sectionNode.ChildNodes.Add(articleNode);

            sectionNode = new TreeNode();
            sectionNode.Text = "Section 2";
            rootNode.ChildNodes.Add(sectionNode);

            articleNode = new TreeNode();
            articleNode.Text = "Article 2";
            sectionNode.ChildNodes.Add(articleNode);

            articleNode = new TreeNode();
            articleNode.Text = "Article 3";
            sectionNode.ChildNodes.Add(articleNode);

            // Add the new node to the Nodes collection of the TreeView.
            _TreeView.Nodes.Add(rootNode);

            _TreeView.ExpandAll();

        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// EnsureVisible ensures that the current article/section are visible in the tree
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void EnsureVisible()
        {
            TreeNode node = new TreeNode();
            string valuePath = Null.NullString;
            ArticleInfo article = this.ParentArticlesModule.Article;

            if (article != null)
            {
                valuePath = "article" + article.ArticleId.ToString();
                valuePath = GetValuePath(valuePath, article.SectionId);
            }
            else
            {
                SectionInfo section = this.ParentArticlesModule.Section;
                if (section != null)
                {
                    valuePath = "section" + section.SectionId.ToString();
                    valuePath = GetValuePath(valuePath, section.ParentSectionId);
                }
            }

            node = _TreeView.FindNode(valuePath);
            if (node != null)
                Expand(node.Parent);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// Expand expands the node in question
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="node">The node to expand</param>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void Expand(TreeNode node)
        {
            if (node != null)
            {
                node.Expand();
                Expand(node.Parent);
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// GetValuePath gets the path for a section
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="path">The current path</param>
        /// <param name="sectionId">The section Id</param>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private string GetValuePath(string path, int sectionId)
        {
            if (sectionId > Null.NullInteger)
            {
                SectionInfo section = SectionController.GetSection(sectionId);
                if (section != null)
                {
                    path = "section" + section.SectionId.ToString() + _TreeView.PathSeparator + path;
                    path = GetValuePath(path, section.ParentSectionId);
                }
            }

            return path;
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// PopulateChildNodes gets the child nodes for a particular TreeNode
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void PopulateChildNodes(TreeNode node)
        {
            int sectionId = Int32.Parse(node.Value.Substring(7));

            //Get any Sections that are children of this node and add them first
            List<SectionInfo> sections = SectionController.GetSectionsByParentSection(ModuleId, sectionId);

            foreach (SectionInfo section in sections)
            {
                AddSectionNode(node.ChildNodes, section);
            }

            //Get any Articles that are children of this node
            List<ArticleInfo> articles = ArticleController.GetArticlesBySection(ModuleId,sectionId);

            foreach (ArticleInfo article in articles)
            {
                AddArticleNode(node.ChildNodes, article);
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// PopulateRootNodes gets the root nodes for the control
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        private void PopulateRootNodes()
        {
            //Get the root sections
            List<SectionInfo> sections = SectionController.GetSectionsByParentSection(ModuleId, Null.NullInteger);

            foreach (SectionInfo section in sections)
            {
                AddSectionNode(_TreeView.Nodes, section);
            }

            //Add special My Articles node if Moderation is enabled

        }

        #endregion

        #region Protected Methods

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// CreateChildControls creates the child control collection
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <history>
        /// 	[cnurse]	04/03/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        protected override void CreateChildControls()
        {
            Controls.Clear();

            //Create TreeView and load defaults
            _TreeView = new TreeView();
            _TreeView.ID = "treeView";
            _TreeView.ExpandDepth = 0;
            _TreeView.ImageSet = TreeViewImageSet.Simple;
            _TreeView.NodeIndent = 20;
            _TreeView.NodeWrap = false;
            _TreeView.PathSeparator = '/';
            _TreeView.PopulateNodesFromClient = false;
            _TreeView.ShowCheckBoxes = TreeNodeTypes.None;
            _TreeView.ShowLines = false;
            _TreeView.ShowExpandCollapse =  true;

            //Add the controls to the collection
            Controls.Add(_TreeView);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// OnPreRender runs just before the control is rendered
        /// </summary>
        /// <history>
        /// 	[cnurse]	03/31/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        protected override void OnPreRender(EventArgs e)
        {

            EnsureChildControls();
            
            //Clear the current Nodes collection
            _TreeView.Nodes.Clear();

            //Load the root nodes
            if (DesignMode)
                CreateDesignTimeNodes();
            else
                PopulateRootNodes();

            base.OnPreRender(e);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// Render renders the control
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="writer">An HtmlTextWriter</param>
        /// <history>
        /// 	[cnurse]	03/31/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        protected override void Render(HtmlTextWriter writer)
        {
            if (!DesignMode && Page != null)
                Page.VerifyRenderingInServerForm(this);

            base.Render(writer);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// RenderContents renders the contents of the control
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// <param name="writer">An HtmlTextWriter</param>
        /// <history>
        /// 	[cnurse]	03/18/2006	created
        /// </history>
        /// -----------------------------------------------------------------------------
        protected override void RenderContents(HtmlTextWriter writer)
        {
            EnsureChildControls();

            EnsureVisible();

            //Render the Caption
            RenderCaption(writer);

            //Render the TreeView
            _TreeView.RenderControl(writer);

        }

        #endregion

    }
}
